package com.k.kc.util.http.httpcomponents;

import com.k.kc.util.UrlUtils;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @author kivil
 */
public class HttpUtils {

    private static SSLConnectionSocketFactory sslConnectionSocketFactory = null;
    private static PoolingHttpClientConnectionManager cm = null;

    static {
        try {
            SSLContextBuilder builder = new SSLContextBuilder();
            // 全部信任 不做身份鉴定
            builder.loadTrustMaterial(null, (TrustStrategy) (x509Certificates, s) -> true);
            sslConnectionSocketFactory = new SSLConnectionSocketFactory(builder.build(), new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
            Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", new PlainConnectionSocketFactory())
                    .register("https", sslConnectionSocketFactory)
                    .build();
            cm = new PoolingHttpClientConnectionManager(registry);
            //max connection
            int maxConnection = 200;
            cm.setMaxTotal(maxConnection);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void setupRequestConfig(HttpRequestBase requestBase) {
        int connectRequestTimeout = 50 * 1000;
        int connectTimeout = 50 * 1000;
        int socketTimeout = 50 * 1000;
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(connectTimeout)
                .setConnectionRequestTimeout(connectRequestTimeout)
                .setSocketTimeout(socketTimeout)
                .setRedirectsEnabled(true)
                .build();
        requestBase.setConfig(requestConfig);
    }

    private static void setupHeaders(HttpRequestBase requestBase, Map<String, String> headers) {
        if (headers != null && headers.size() > 0) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                requestBase.setHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    private static ResponseHandler<ResponseData> resultHandlerResponseData() {
        return httpResponse -> {
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            Map<String, String> respHeader = new HashMap<>();
            HeaderIterator iter = httpResponse.headerIterator();
            while (iter.hasNext()) {
                org.apache.http.Header header = iter.nextHeader();
                respHeader.put(header.getName(), header.getValue());
            }
            String data = EntityUtils.toString(httpResponse.getEntity(), UTF_8);
            return new ResponseData(statusCode + "", respHeader, data);
        };
    }

    private static ResponseHandler<Integer> resultHandlerCode() {
        return httpResponse -> httpResponse.getStatusLine().getStatusCode();
    }

    private static ResponseHandler<String> resultHandlerData() {
        return httpResponse -> EntityUtils.toString(httpResponse.getEntity(), UTF_8);
    }

    /**
     * 使用此方法需要关心资源关闭问题
     *
     * @param client      client
     * @param requestBase request
     * @return result
     * @throws IOException e
     */
    private static String request(CloseableHttpClient client, HttpUriRequest requestBase) throws IOException {
        String result = null;
        HttpEntity entity = null;
        try (CloseableHttpResponse response = client.execute(requestBase)) {
            System.out.println(response.getStatusLine());
            HeaderIterator iter = response.headerIterator();
            while (iter.hasNext()) {
                org.apache.http.Header header = iter.nextHeader();
                System.out.println(header.getName() + ":" + header.getValue());
            }
            entity = response.getEntity();
            result = EntityUtils.toString(entity, UTF_8);
            EntityUtils.consume(entity);
        } catch (Exception e) {
            EntityUtils.consume(entity);
        }
        return result;
    }

    /**
     * 使用此方法不用关心资源问题
     *
     * @param client          client
     * @param requestBase     requestBase
     * @param responseHandler responseHandler
     * @param <T>             type
     * @return Result
     * @throws IOException e
     */
    private static <T> T request(CloseableHttpClient client, HttpUriRequest requestBase, ResponseHandler<? extends T> responseHandler) throws IOException {
        return client.execute(requestBase, responseHandler);
    }

    private static void setupParamsForJson(HttpEntityEnclosingRequestBase requestBase, String jsonParams) {
        requestBase.setEntity(new StringEntity(jsonParams, ContentType.create("application/json", "utf-8")));
    }

    private static void setupParamsForForm(HttpEntityEnclosingRequestBase requestBase, Map<String, String> paramsMap) throws UnsupportedEncodingException {
        if (paramsMap != null && paramsMap.size() > 0) {
            List<NameValuePair> params = new ArrayList<>();
            for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
                params.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            requestBase.setEntity(new UrlEncodedFormEntity(params));
        }
    }

    private static void setupParamsForFile(HttpEntityEnclosingRequestBase requestBase, String key, String filePath, String fileName) {
        FileBody fileBody = new FileBody(new File(filePath), ContentType.MULTIPART_FORM_DATA, fileName);
        MultipartEntityBuilder multipartEntity = MultipartEntityBuilder.create();
        multipartEntity.addPart(key, fileBody);
        requestBase.setEntity(multipartEntity.build());
    }

    private static String getUrlWithQueryString(String uri, Map<String, String> paramsMap) {
        if (paramsMap != null && paramsMap.size() > 0) {
            return UrlUtils.buildUriQueryParameters(uri, paramsMap);
        } else {
            return uri;
        }
    }

    private static CloseableHttpClient getHttpClient() {
        return HttpClients.custom()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .setConnectionManager(cm)
                .setConnectionManagerShared(true)
                .build();
    }

    public static String doGet1(String url, Map<String, String> headerMap, Map<String, String> paramsMap) throws IOException {
        String resultStr = "";
        CloseableHttpClient httpclient;
        HttpGet req;
        CloseableHttpResponse resp;
        HttpEntity resultEntity = null;
        try {
            httpclient = getHttpClient();
            req = new HttpGet(getUrlWithQueryString(url, paramsMap));
            resp = httpclient.execute(req);
            resultEntity = resp.getEntity();
            resultStr = EntityUtils.toString(resultEntity, UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            EntityUtils.consume(resultEntity);
        }
        return resultStr;
    }

    public static String doGet2(String url, Map<String, String> headerMap, Map<String, String> paramsMap) {
        String stringResult = null;
        byte[] byteArrayResult;
        try (CloseableHttpClient httpclient = getHttpClient();
             CloseableHttpResponse resp = httpclient.execute(new HttpGet(getUrlWithQueryString(url, paramsMap)))) {

            // result type 1
            stringResult = EntityUtils.toString(resp.getEntity(), UTF_8);
            System.out.println(stringResult);

            // result type 2
            byteArrayResult = EntityUtils.toByteArray(resp.getEntity());
            System.out.println(Arrays.toString(byteArrayResult));
            EntityUtils.consume(resp.getEntity());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return stringResult;
    }

    public static String doGet(String url, Map<String, String> headerMap, Map<String, String> paramsMap) throws IOException {
        String result;
        HttpEntity entity = null;
        try (CloseableHttpClient httpclient = getHttpClient()) {
            // getUrl with queryString params
            HttpGet httpGet = new HttpGet(getUrlWithQueryString(url, paramsMap));
            // setHeaders
            setupHeaders(httpGet, headerMap);
            CloseableHttpResponse resp = httpclient.execute(httpGet);
            entity = resp.getEntity();
            result = EntityUtils.toString(entity);
        } finally {
            EntityUtils.consume(entity);
        }
        return result;
    }

    public static String doGet(String url, Map<String, String> paramsMap) throws IOException {
        return doGet(url, null, paramsMap);
    }

    public static String doGet(String url) throws IOException {
        return doGet(url, null, null);
    }

    public static String doPost(String url, Map<String, String> headerMap, Map<String, String> paramsMap) throws IOException {
        String result;
        try (CloseableHttpClient httpclient = getHttpClient()) {
            HttpPost httpPost = new HttpPost(url);
            // headers
            setupHeaders(httpPost, headerMap);
            // reqParams
            setupParamsForForm(httpPost, paramsMap);
            // getResult
            result = request(httpclient, httpPost);
        }
        return result;
    }

    public static String doPostForm(String url, Map<String, String> headerMap) throws IOException {
        String result;
        try (CloseableHttpClient httpclient = getHttpClient()) {
            HttpPost httpPost = new HttpPost(url);
            // set config
            setupRequestConfig(httpPost);
            // set default headers
            httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
            // set headers
            setupHeaders(httpPost, headerMap);
            // set  reqParams
            setupParamsForForm(httpPost, headerMap);
            // getResult
            result = request(httpclient, httpPost);
        }
        return result;
    }

    public static String doPostJson(String url, Map<String, String> headerMap, String jsonParams) throws IOException {
        String result;
        try (CloseableHttpClient httpclient = getHttpClient()) {
            HttpPost httpPost = new HttpPost(url);
            // set config
            setupRequestConfig(httpPost);
            // set default headers
            httpPost.setHeader("Content-Type", "application/json");
            // set headers
            setupHeaders(httpPost, headerMap);
            // set  reqParams
            setupParamsForJson(httpPost, jsonParams);
            // getResult
            result = request(httpclient, httpPost);
        }
        return result;
    }

    public static String doPostJson(String url, String jsonParams) throws IOException {
        return doPostJson(url, null, jsonParams);
    }

    public static String postUpload(String url, String key, String localFile, String filename) {
        System.out.println("httpUpload: url:" + url + " localFile:" + localFile);
        String result = null;
        try (CloseableHttpClient httpClient = getHttpClient()) {
            HttpPost httpPost = new HttpPost(url);
            // set request config
            setupRequestConfig(httpPost);
            // set params
            setupParamsForFile(httpPost, key, localFile, filename);
            // get result
            result = request(httpClient, httpPost);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public static class ResponseData {
        private String code;
        private Map<String, String> header;
        private String data;

        public ResponseData() {
        }

        public ResponseData(String code, Map<String, String> header, String data) {
            this.code = code;
            this.header = header;
            this.data = data;
        }

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public Map<String, String> getHeader() {
            return header;
        }

        public void setHeader(Map<String, String> header) {
            this.header = header;
        }

        public String getData() {
            return data;
        }

        public void setData(String data) {
            this.data = data;
        }

        @Override
        public String toString() {
            return "ResponseData{" +
                    "code='" + code + '\'' +
                    ", header=" + header +
                    ", data='" + data + '\'' +
                    '}';
        }
    }

    public static void main(String[] args) {
        try {
//            String url = "http://localhost:18080/login/query/a1";
//            url = "http://www.baidu.com";
//            String result = doGet(url);
//            System.out.println(result);
            String url = "http://10.110.12.33:8090/kafka/topic/testjs";
            try (CloseableHttpClient httpclient = getHttpClient()) {
                HttpPost httpPost = new HttpPost(url);
                // set config
                setupRequestConfig(httpPost);
                // set default headers
                httpPost.setHeader("Content-Type", "application/json");
                // set headers
                setupHeaders(httpPost, null);
                // set  reqParams
                setupParamsForJson(httpPost, "{\"a\":\"a111\"}");
                // getResult
                ResponseData responseData = request(httpclient, httpPost, resultHandlerResponseData());
                System.out.println(responseData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
